#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/ioctl.h>

#define BUFSIZE   32 /* 设置最大偏移量为64, 方便打印完整的内存空间数据*/
#define CMD_TEST0 _IO('S', 0)
#define CMD_TEST1 _IOW('S', 1, int)
#define CMD_TEST2 _IOR('S', 2, int)
#define CMD_TEST3 _IOW('S', 3, int)

struct __CMD_TEST{
	int a;
	int b;
	int c;
};

static char usrdata[] = {"sumu"};

void usage_info(void)
{
	printf("\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
	printf("+       help information  @sumu          +\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");

	printf("help:\n");
	printf("use format: ./app_name /dev/device_name arg1 ... \n");
	printf("            ./chrdev_data_exchange_demo_app.out /dev/sdevice 1 x # 从x位置读取 \n");
	printf("            ./chrdev_data_exchange_demo_app.out /dev/sdevice 2 x # 从x位置写入 \n");
	printf("            ./chrdev_data_exchange_demo_app.out /dev/sdevice 3 x # x为任意值 \n");
    printf("            驱动中buf最大为1024字节 \n");
	printf("\n");

	printf("command info:\n");
	printf("  (1)load module  : insmod module_name.ko\n");
	printf("  (2)unload module: rmmod module_name.ko\n");
	printf("  (3)show module  : lsmod\n");
	printf("  (4)view device  : cat /proc/devices\n");
	printf("  (5)create device node: mknod /dev/device_name c major_num secondary_num \n");
	printf("  (6)show device node  : ls /dev/device_name \n");
	printf("  (7)show device vlass  : ls /sys/class \n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
}

int main(int argc, char *argv[])
{
    int fd = -1;
    int ret = 0;
    char *filename = NULL;
    unsigned int arg1 = 0;
    unsigned int arg2 = 0;

    char readbuf[BUFSIZE] = {0};
    char writebuf[BUFSIZE] = {0};

    unsigned int off1 = 0; // 定义读写偏移位置
    unsigned int off2 = 0; // 定义读写偏移位置
    unsigned int off = 0; // 定义读写偏移位置

    printf("*** Build Time: %s %s,Git Version: %s Git Remote: %s***\n", 
            __DATE__, __TIME__, GIT_VERSION, GIT_PATH);
    // ./xxx.out /dev/sdevice x x
    if (argc != 4) 
    {
        usage_info();
        return -1;
    }
    // 解析参数
    filename = argv[1];
    arg1 = atoi(argv[2]);
    arg2 = atoi(argv[3]);
    printf("%s %s %d %d\n", argv[0], filename, arg1, arg2);

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("can't open file %s !\n", filename);
        return -1;
    }

    off1 = lseek(fd, 0, SEEK_END); // 读取字符设备文件可读写最大的大小
    printf("%s mem buf size is %d\n", filename, off1);
    if (arg1 == 1)
    { 
        off = arg2;// 获取读写的偏移位置
        /* 从驱动文件读取数据 */
        ret = lseek(fd, off, SEEK_SET); // 将偏移量设置为距离起始地址 off的位置
        if (ret < 0)
        {
            printf("lseek %s %d failed!\n", filename, off);
        }
        
        ret = read(fd, readbuf, BUFSIZE);
        if (ret < 0)
        {
            printf("read file %s failed!\n", filename);
        }
        off2 = lseek(fd, 0, SEEK_CUR); // 读取当前位置的偏移量
        /*  读取成功，打印出读取成功的数据 */
        printf("read data \"%s\" from %s! off=%d off2=%d\n", readbuf, filename, off, off2);

    }
    else if (arg1 == 2)
    {
        off = arg2;// 获取读写的偏移位置
        ret = lseek(fd, off, SEEK_SET); // 将偏移量设置为距离起始地址 off的位置
        if (ret < 0)
        {
            printf("lseek %s %d failed!\n", filename, off);
        }

        /* 向设备驱动写数据 */
        memcpy(writebuf, usrdata, sizeof(usrdata));
        ret = write(fd, writebuf, sizeof(usrdata));
        if (ret < 0)
        {
            printf("write file %s failed!\n", filename);
        }
        off2 = lseek(fd, 0, SEEK_CUR); // 读取当前位置的偏移量
        printf("write \"%s\" to %s success!off=%d off2=%d\n", usrdata, filename, off, off2);
    }
    else if (arg1 == 3)
    {
        int val = 0;
        struct __CMD_TEST cmd_test3 = {2, 3, 5};
        ioctl(fd,CMD_TEST0);
        ioctl(fd,CMD_TEST1, 1);
        ioctl(fd,CMD_TEST2, &val);
        printf("val = %d\n", val);

        ioctl(fd,CMD_TEST3, &cmd_test3);
    }
    /* 关闭设备 */
    ret = close(fd);
    if (ret < 0)
    {
        printf("can't close file %s !\n", filename);
        return -1;
    }

    return 0;
}